package com.self.cloudmall.oms.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alipay.api.AlipayApiException;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.google.common.collect.Lists;
import com.self.cloudmall.common.constant.OrderConstant;
import com.self.cloudmall.common.constant.PmsConstant;
import com.self.cloudmall.common.dto.*;
import com.self.cloudmall.common.enumm.OrderConfirmStateEnum;
import com.self.cloudmall.common.enumm.OrderStatusEnum;
import com.self.cloudmall.common.utils.R;
import com.self.cloudmall.common.utils.ThreadLocalUtil;
import com.self.cloudmall.common.utils.UUIDUtil;
import com.self.cloudmall.oms.client.CartProductClient;
import com.self.cloudmall.oms.client.ProductAttrFeignClient;
import com.self.cloudmall.oms.client.ReceAddressClient;
import com.self.cloudmall.oms.client.WmsClient;
import com.self.cloudmall.oms.config.AlipayTemplate;
import com.self.cloudmall.oms.entity.PaymentInfoEntity;
import com.self.cloudmall.oms.service.PaymentInfoService;
import com.self.cloudmall.oms.vo.*;
import com.self.cloudmall.oms.entity.OrderItemEntity;
import com.self.cloudmall.oms.service.OrderItemService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.self.cloudmall.oms.utils.PageUtils;
import com.self.cloudmall.oms.utils.Query;

import com.self.cloudmall.oms.mapper.OrderMapper;
import com.self.cloudmall.oms.entity.OrderEntity;
import com.self.cloudmall.oms.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Slf4j
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderEntity> implements OrderService {

    @Autowired
    private ReceAddressClient receAddressClient;
    @Autowired
    private CartProductClient cartProductClient;
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    @Autowired
    private WmsClient wmsClient;
    @Autowired
    private ProductAttrFeignClient productAttrFeignClient;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    @Autowired
    private OrderItemService orderItemService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private AlipayTemplate alipayTemplate;
    @Autowired
    private PaymentInfoService paymentInfoService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public PageUtils orderList(Map<String, Object> params) {
        LoginUserDto userDto = (LoginUserDto)ThreadLocalUtil.get();
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>().lambda()
                        .eq(OrderEntity::getMemberId,userDto.getId())
                        .orderByDesc(OrderEntity::getId)
        );
        List<OrderEntity> records = page.getRecords();
        if (!CollectionUtils.isEmpty(records)) {
            List<String> collect = records.stream().map(OrderEntity::getOrderSn).collect(Collectors.toList());
            List<OrderItemEntity> list = orderItemService.lambdaQuery().in(OrderItemEntity::getOrderSn, collect).list();
            records.forEach(x -> {
                for (OrderItemEntity item : list) {
                    if (x.getOrderSn().equals(item.getOrderSn())) {
                        List<OrderItemEntity> items = x.getItems();
                        if (CollectionUtils.isEmpty(items)) {
                            items = new ArrayList<>();
                        }
                        items.add(item);
                        x.setItems(items);
                    }
                }
            });
            page.setRecords(records);
        }
        return new PageUtils(page);
    }

    @Override
    public R handlePayed(PayAsyncVo payAsyncVo) {
        //保存交易流水
        PaymentInfoEntity paymentInfo = new PaymentInfoEntity();
        paymentInfo.setAlipayTradeNo(payAsyncVo.getTrade_no());
        paymentInfo.setOrderSn(payAsyncVo.getOut_trade_no());
        paymentInfo.setTotalAmount(new BigDecimal(payAsyncVo.getTotal_amount()));
        paymentInfo.setCallbackTime(payAsyncVo.getNotify_time());
        paymentInfo.setPaymentStatus(payAsyncVo.getTrade_status());
        paymentInfoService.save(paymentInfo);

        //修改订单状态
        if ("TRADE_SUCCESS".equalsIgnoreCase(payAsyncVo.getTrade_status())
        || "TRADE_FINISHED".equalsIgnoreCase(payAsyncVo.getTrade_status())){
             OrderEntity entity = new OrderEntity();
             entity.setStatus(OrderStatusEnum.PAYED.getCode());
             this.lambdaUpdate().eq(OrderEntity::getOrderSn,payAsyncVo.getOut_trade_no())
                     .update(entity);
        }
        return R.ok();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void createSeckillOrder(SeckillOrderDto seckillOrder) throws Exception {
        LoginUserDto loginUser = ThreadLocalUtil.get();
        if (seckillOrder.getAddrId() == null){
            R r = receAddressClient.getAddressList(loginUser.getId());
            if (r.isSuccess()) {
                List<ReceAddressDto> receAddressVos = JSONObject.parseObject(JSONObject.toJSONString(r.getData()), new TypeReference<List<ReceAddressDto>>() {
                });
                for (ReceAddressDto address : receAddressVos){
                    if (address.getDefaultStatus() == PmsConstant.DEFAULT){
                        seckillOrder.setAddrId(address.getId());
                        break;
                    }
                }
            }
        }
        OrderEntity entity = new OrderEntity();
        //设置订单号
        BeanUtils.copyProperties(seckillOrder,entity);
        entity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        entity.setAutoConfirmDay(OrderConstant.ORDER_AUTO_CONFIRM_TIME);
        entity.setConfirmStatus(OrderConfirmStateEnum.UNCONFIRM.getCode());
        entity.setModifyTime(new Date());
        //设置收货地址
        R receR = buildReceAddr(seckillOrder.getAddrId(),entity);
        if (!receR.isSuccess()){
            throw new Exception((String)receR.get("msg"));
        }
        //设置订单项
        List<OrderItemEntity> orderItems = Lists.newArrayList();
        R itemsR = buildOrderItems(orderItems, seckillOrder.getOrderSn());
        if (!itemsR.isSuccess()){
            throw new Exception((String)itemsR.get("msg"));
        }
        //计算金额相关属性
        computeAmount(entity,orderItems);
        //保存订单和订单项
        this.save(entity);
        orderItemService.saveBatch(orderItems);
        //库存锁定
        WareSkuLockDto skuLockDto = new WareSkuLockDto();
        skuLockDto.setOrderSn(seckillOrder.getOrderSn());
        List<SkuLockDto> collect = orderItems.stream().map(x -> {
            SkuLockDto lockDto = new SkuLockDto();
            lockDto.setSkuId(x.getSkuId());
            lockDto.setSkuTitle(x.getSkuName());
            lockDto.setCount(x.getSkuQuantity());
            return lockDto;
        }).collect(Collectors.toList());
        skuLockDto.setSkuLocks(collect);
        R lockR = wmsClient.orderLockStock(skuLockDto);
        if (!lockR.isSuccess()){
            throw new Exception("锁定库存异常");
        }
        //订单创建成功，发送延迟消息30分钟后，订单自动取消，自动解锁库存
        OrderDto orderDto = new OrderDto();
        BeanUtils.copyProperties(entity,orderDto);
        rabbitTemplate.convertAndSend("order-event-exchange","order.create",orderDto);
    }

    @Override
    public OrderConfirmVo getOrderConfirm() {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        LoginUserDto loginUser = ThreadLocalUtil.get();
        /**
         * 异步线程导致，feign调用配置requestInterceptor失效，
         * 因为每个feign都有一个私有threadlocal线程与主线程的数据不同步，
         */
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //获取当前用户的收货地址
        CompletableFuture<Void>  receAddressFuture = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            R r = receAddressClient.getAddressList(loginUser.getId());
            if (r.isSuccess()) {
                List<ReceAddressDto> receAddressVos = JSONObject.parseObject(JSONObject.toJSONString(r.getData()), new TypeReference<List<ReceAddressDto>>() {
                });
                orderConfirmVo.setReceAddressList(receAddressVos);
            }
        }, threadPoolExecutor);

        //获取购物车数据
        CompletableFuture<Void>  orderItemFuture = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            R cartR = cartProductClient.getCurrentCartItems();
            if (cartR.isSuccess()){
                List<OrderItemVo> orderItemVos = JSONObject.parseObject(JSONObject.toJSONString(cartR.getData()), new TypeReference<List<OrderItemVo>>() {});
                orderConfirmVo.setOrderItemList(orderItemVos);
            }
        }, threadPoolExecutor).thenRunAsync(() ->{
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<OrderItemVo> orderItemList = orderConfirmVo.getOrderItemList();
            if (!CollectionUtils.isEmpty(orderItemList)){
                List<Long> collect = orderItemList.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
                R<Map<Long, Boolean>> mapR = wmsClient.hasStock(collect);
                if (mapR.isSuccess()){
                    orderConfirmVo.setHasStocks(mapR.getData());
                }
            }
        }, threadPoolExecutor);

        //积分
        orderConfirmVo.setIntegration(loginUser.getIntegration());

        //等待所有执行完成
        try {
            CompletableFuture.allOf(receAddressFuture,orderItemFuture).get();
        } catch (Exception e) {
           log.error("组装订单数据异常：",e);
        }
        //防重复提交
        String uuid = UUIDUtil.generateUUID();
        orderConfirmVo.setOrderToken(uuid);
        redisTemplate.opsForValue().set(OrderConstant.ORDER_TOKEN_PREFIX+loginUser.getId(),
                uuid,OrderConstant.ORDER_TOKEN_EXPIRE, TimeUnit.SECONDS);

        return orderConfirmVo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public R submitOrder(SubmitOrderVo submitOrder) throws Exception {
        LoginUserDto loginUser = ThreadLocalUtil.get();
        //校验orderToken，应付金额，锁库存
        //lua脚本，使用判断和删除原子性操作
        String redisScript = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        Long execute = redisTemplate.execute(new DefaultRedisScript<>(redisScript, Long.class), Collections.singletonList(OrderConstant.ORDER_TOKEN_PREFIX + loginUser.getId()),
                submitOrder.getOrderToken());
        if (execute == 0L){
            return R.error("重复提交失败");
        }
        OrderEntity entity = new OrderEntity();
        //设置订单号
        String id = IdWorker.getTimeId();
        entity.setOrderSn(id);
        entity.setMemberId(loginUser.getId());
        entity.setMemberUsername(loginUser.getUsername());
        entity.setPayType(submitOrder.getPayType());
        entity.setSourceType(submitOrder.getSourceType());
        entity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        entity.setAutoConfirmDay(OrderConstant.ORDER_AUTO_CONFIRM_TIME);
        entity.setNote(submitOrder.getNote());
        entity.setConfirmStatus(OrderConfirmStateEnum.UNCONFIRM.getCode());
        entity.setModifyTime(new Date());
        //设置收货地址
        R receR = buildReceAddr(submitOrder.getAddrId(),entity);
        if (!receR.isSuccess()){
            return receR;
        }
        //设置订单项
        List<OrderItemEntity> orderItems = Lists.newArrayList();
        R itemsR = buildOrderItems(orderItems, id);
        if (!itemsR.isSuccess()){
            return itemsR;
        }
        //计算金额相关属性
        computeAmount(entity,orderItems);
        //验价
        BigDecimal subtract = entity.getPayAmount().subtract(submitOrder.getPayAmount());
        if (Math.abs(subtract.doubleValue()) >= 0.01){
            return R.error("应付金额不正确");
        }
        //保存订单和订单项
        this.save(entity);
        orderItemService.saveBatch(orderItems);
        //库存锁定
        WareSkuLockDto skuLockDto = new WareSkuLockDto();
        skuLockDto.setOrderSn(id);
        List<SkuLockDto> collect = orderItems.stream().map(x -> {
            SkuLockDto lockDto = new SkuLockDto();
            lockDto.setSkuId(x.getSkuId());
            lockDto.setSkuTitle(x.getSkuName());
            lockDto.setCount(x.getSkuQuantity());
            return lockDto;
        }).collect(Collectors.toList());
        skuLockDto.setSkuLocks(collect);
        R lockR = wmsClient.orderLockStock(skuLockDto);
        if (!lockR.isSuccess()){
            throw new Exception("锁定库存异常");
        }
        //订单创建成功，发送延迟消息30分钟后，订单自动取消，自动解锁库存
        OrderDto orderDto = new OrderDto();
        BeanUtils.copyProperties(entity,orderDto);
        rabbitTemplate.convertAndSend("order-event-exchange","order.create",orderDto);
        return R.ok().setData(entity);
    }

    @Override
    public void orderClose(OrderDto order) throws Exception {
        OrderEntity entity = this.baseMapper.selectById(order.getId());
        if (entity != null && entity.getStatus() == OrderStatusEnum.CREATE_NEW.getCode().intValue()){
            OrderEntity orderEntity = new OrderEntity();
            orderEntity.setId(entity.getId());
            orderEntity.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(orderEntity);
            //解锁存储，因为订单关闭可能处理时间很长或者卡住，超过20分钟自动解锁库存的时间，
            //就造成库存不能解锁库存
            rabbitTemplate.convertAndSend("order-event-exchange","order.release.other",order);
        }
    }

    @Override
    public R pay(String orderSn) {
        OrderEntity one = this.lambdaQuery().eq(OrderEntity::getStatus, OrderStatusEnum.CREATE_NEW.getCode())
                .eq(OrderEntity::getOrderSn, orderSn)
                .one();
        if (one == null){
            return R.error("订单不存在，请查看订单状态").setData(one);
        }
        List<OrderItemEntity> orderItemEntities = orderItemService.lambdaQuery().eq(OrderItemEntity::getOrderSn, orderSn)
                .list();
        OrderItemEntity orderItemEntity = orderItemEntities.get(0);
        PayVo vo = new PayVo();
        vo.setOutTradeNo(orderSn);
        vo.setSubject(orderItemEntity.getSkuName());
        vo.setBody(orderItemEntity.getSkuAttrsVals());
        vo.setTotalAmount(one.getPayAmount().toString());
        try {
            String pay = alipayTemplate.pay(vo);
            return R.ok(pay).setData(pay);
        } catch (AlipayApiException e) {
            log.error("支付宝支付异常：{}",e);
            return R.error("支付宝支付异常,请重新支付").setData(one);
        }
    }

    private void computeAmount(OrderEntity entity, List<OrderItemEntity> orderItems) {
        BigDecimal coupon = BigDecimal.ZERO;
        BigDecimal promotion = BigDecimal.ZERO;
        BigDecimal integration = BigDecimal.ZERO;
        BigDecimal total = BigDecimal.ZERO;
        Integer integ = 0;
        Integer growth = 0;
        for (OrderItemEntity item : orderItems){
            coupon = coupon.add(item.getCouponAmount());
            promotion = promotion.add(item.getPromotionAmount());
            integration = integration.add(item.getIntegrationAmount());
            total = total.add(item.getRealAmount());
            integ += item.getGiftIntegration();
            growth += item.getGiftGrowth();
        }
        entity.setCouponAmount(coupon);
        entity.setPromotionAmount(promotion);
        entity.setIntegrationAmount(integration);
        entity.setTotalAmount(total);
        entity.setPayAmount(total.add(entity.getFreightAmount()));
        entity.setIntegration(integ);
        entity.setGrowth(growth);
    }

    private R buildOrderItems(List<OrderItemEntity> orderItems,String id) {
        R cartR = cartProductClient.getCurrentCartItems();
        if (!cartR.isSuccess()){
            return R.error("获取订单项异常");
        }
        List<OrderItemVo> orderItemVos = JSONObject.parseObject(JSONObject.toJSONString(cartR.getData()), new TypeReference<List<OrderItemVo>>() {});
        for (OrderItemVo x : orderItemVos){
            OrderItemEntity itemEntity = new OrderItemEntity();
            itemEntity.setOrderSn(id);
            //设置sku信息
            itemEntity.setSkuId(x.getSkuId());
            itemEntity.setSkuName(x.getSkuTitle());
            itemEntity.setSkuPic(x.getImage());
            itemEntity.setSkuPrice(x.getPrice());
            itemEntity.setSkuQuantity(x.getCount());
            itemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(x.getSkuAttrs(),","));
            //设置spu信息
            R spuR = productAttrFeignClient.getSpuInfo(x.getSkuId());
            if (!spuR.isSuccess()){
                return spuR;
            }
            SpuInfoDto spuInfo = JSONObject.parseObject(JSONObject.toJSONString(spuR.getData()), SpuInfoDto.class);
            itemEntity.setSpuBrand(spuInfo.getBrandId()+"");
            itemEntity.setSpuId(spuInfo.getId());
            itemEntity.setSpuName(spuInfo.getSpuName());
            itemEntity.setCategoryId(spuInfo.getCatalogId());
            //TODO:促销，优惠不做
            BigDecimal zero = BigDecimal.ZERO;
            //优惠金额
            itemEntity.setCouponAmount(zero);
            //促销金额
            itemEntity.setPromotionAmount(zero);
            //积分金额
            itemEntity.setIntegrationAmount(zero);
            //积分，成长值
            itemEntity.setGiftIntegration(x.getTotalPrice().intValue());
            itemEntity.setGiftGrowth(x.getTotalPrice().intValue());
            //实付金额
            BigDecimal realAmount = x.getTotalPrice().subtract(itemEntity.getCouponAmount())
                    .subtract(itemEntity.getPromotionAmount())
                    .subtract(itemEntity.getIntegrationAmount());
            if(realAmount.compareTo(BigDecimal.ZERO) < 0 ){
                return R.error("实付金额小于零");
            }
            itemEntity.setRealAmount(realAmount);
            orderItems.add(itemEntity);
        }
        return R.ok();
    }

    private R buildReceAddr(Long addrId,OrderEntity entity) {
        R fareR = wmsClient.getFare(addrId);
        Object data = fareR.getData();
        if (!fareR.isSuccess() || data== null){
            return R.error("获取收货地址异常");
        }
        ReceAddrFareDto addrFareDto = JSONObject.parseObject(JSONObject.toJSONString(data), ReceAddrFareDto.class);
        ReceAddressDto receAddressDto = addrFareDto.getReceAddress();
        entity.setReceiverCity(receAddressDto.getCity());
        entity.setReceiverDetailAddress(receAddressDto.getDetailAddress());
        entity.setReceiverName(receAddressDto.getName());
        entity.setReceiverPhone(receAddressDto.getPhone());
        entity.setReceiverProvince(receAddressDto.getProvince());
        entity.setReceiverPostCode(receAddressDto.getPostCode());
        entity.setReceiverRegion(receAddressDto.getAreacode());
        //设置运费
        entity.setFreightAmount(addrFareDto.getFare());
        return R.ok();
    }

}